package br.edu.ufcg.lp2;

import static org.junit.Assert.*;

import java.util.ArrayList;

import org.junit.Before;
import org.junit.Test;

public class NameTest {
	
	Name n1;
	Name n2;
	Name n3;
	
	Tag t1;
	Tag t2;
	Tag t3;
	Tag t4;
	
	User u1;
	User u2;
	User u3;
	User u4;
	User u5;
	User u6;
	
	Vote v1;
	Vote v2;
	Vote v3;
	Vote v4;
	Vote v5;
	Vote v6;

	@Before
	public void setUp() throws Exception {
		// Our names to be used in the tests :)
		n1 = new Name("Franco", "Origem: Latim. Significado: Homem livre");
		n2 = new Name("Joao", "Origem: Biblico. Significado: Deus e bondoso");
		n3 = new Name("Fellype", "Origem: Campina Grande. Significado: Afrescuramento de Felipe");
		
		// Tags to our names
		t1 = new Tag("masculino");
		t2 = new Tag("latim");
		t3 = new Tag("biblico");
		t4 = new Tag("invencao");
		
		// Adding the tags to the names
		n1.addTag(t1);
		n1.addTag(t2);
		n2.addTag(t1);
		n2.addTag(t3);
		n3.addTag(t1);
		n3.addTag(t4);
		
		// Three users that shall evaluate the names through voting
		u1 = new User("George");
		u2 = new User("John");
		u3 = new User("Paul");
		u4 = new User("Ringo");
		u5 = new User("Lucy");
		u6 = new User("Blue Meanie");
		
		// Three votes...
		v1 = new Vote(1, u1);
		v2 = new Vote(0, u2);
		v3 = new Vote(-1, u3);
		v4 = new Vote(1, u4);
		v5 = new Vote(0, u5);
		v6 = new Vote(-1, u6);
	}

	@Test
	public void testName() {
		try {
			// Creating a normal name
			n1 = new Name("Stefano", "Origem: Latim. Significado: Coroado com louros");
			assertEquals("Stefano", n1.getName());
			assertEquals("Origem: Latim. Significado: Coroado com louros", n1.getDescription());
			assertEquals(0, n1.getTags().size(), 0.01);
			assertEquals(0, n1.getVotes().size(), 0.01);
			
			// Creating a name with empty name (lol)
			n2 = new Name("", "Origem: Nada. Significado: Nada");
			fail("Did not break with an empty name");
		} catch (Exception e) {
			assertEquals("The name cannot be empty", e.getMessage());
		}
		
		try {
			// Creating a name with empty description
			n3 = new Name("Fellype", "");
			fail("Did not break with an empty description");
		} catch (Exception e) {
			assertEquals("The description cannot be empty", e.getMessage());
		}
		
	}

	@Test
	public void testGetName() throws Exception {
		// GetName test for n1, n2 and n3
		assertEquals("Franco", n1.getName());
		assertEquals("Joao", n2.getName());
		assertEquals("Fellype", n3.getName());
		
		// GetName test after a set
		n1.setName("Boromir");
		n2.setName("Aragorn");
		n3.setName("Gandalf");
		assertEquals("Boromir", n1.getName());
		assertEquals("Aragorn", n2.getName());
		assertEquals("Gandalf", n3.getName());
	}

	@Test
	public void testSetName() {
		try {
			// Asserting n1's name before and after the set
			assertEquals("Franco", n1.getName());
			n1.setName("Boromir");
			assertEquals("Boromir", n1.getName());
			
			// Setting n2's name for an empty string, must catch exception
			assertEquals("Joao", n2.getName());
			n2.setName("");
			fail("Did not break with an empty name");
		} catch (Exception e) {
			assertEquals("The name cannot be empty", e.getMessage());
		}
		
		try {
			// Setting n3's name for a String made entirely of empty spaces
			n3.setName("       ");
			fail("Did not break with a name made of empty spaces");
		} catch (Exception e) {
			assertEquals("The name cannot be empty", e.getMessage());
		}
	}

	@Test
	public void testGetDescription() throws Exception {
		// GetDescription test for n1, n2 and n3
		assertEquals("Origem: Latim. Significado: Homem livre", n1.getDescription());
		assertEquals("Origem: Biblico. Significado: Deus e bondoso", n2.getDescription());
		assertEquals("Origem: Campina Grande. Significado: Afrescuramento de Felipe", n3.getDescription());
				
		// GetDescription test after a set
		n1.setDescription("Homem livre, origem latina");
		n2.setDescription("Deus e bondoso, origem biblica");
		n3.setDescription("Afrescuramento de Felipe, origem campinense");
		assertEquals("Homem livre, origem latina", n1.getDescription());
		assertEquals("Deus e bondoso, origem biblica", n2.getDescription());
		assertEquals("Afrescuramento de Felipe, origem campinense", n3.getDescription());
	}

	@Test
	public void testSetDescription() {
		try {
			// Set and then test for n1
			n1.setDescription("Homem livre, origem latina");
			assertEquals("Homem livre, origem latina", n1.getDescription());
			
			// Setting n2's description for an empty string, must catch exception
			n2.setDescription("");
			fail("Did not break with an empty description");
		} catch (Exception e) {
			assertEquals("The description cannot be empty", e.getMessage());
		}
	}
	
	@Test
	public void testAddTag() throws Exception {
		// Adding a tag and then testing the size of the tags list
		assertEquals(2, n3.getTags().size(), 0.01);
		n3.addTag(new Tag("grego"));
		assertEquals(3, n3.getTags().size(), 0.01);
		n3.addTag(new Tag("sanscrito"));
		assertEquals(4, n3.getTags().size(), 0.01);
	}
	
	@Test
	public void testToString() {
		// ToString test for n1, n2 and n3
		assertEquals("Name: Franco\nDescription: Origem: Latim. Significado: Homem livre", n1.toString());
		assertEquals("Name: Joao\nDescription: Origem: Biblico. Significado: Deus e bondoso", n2.toString());
		assertEquals("Name: Fellype\nDescription: Origem: Campina Grande. Significado: Afrescuramento de Felipe", n3.toString());
	}

	/*
	@Test
	public void testGetRating() throws Exception {
		// Testing name rating before and after a tag set
		assertEquals(10/2., n3.getRating(), 0.01);
		t1.setRating(7);
		t4.setRating(1);
		assertEquals(8/2., n3.getRating(), 0.01);
	}
	*/
	
	@Test
	public void testGetRating() throws Exception {
		// Rating (mean of the votes) test for n1, n2 and n3 before voting
		// The default value for names not yet evaluated by the user is -2
		assertEquals(-2, n1.getRating(), 0.01);
		assertEquals(-2, n2.getRating(), 0.01);
		assertEquals(-2, n3.getRating(), 0.01);
		
		// Rating (mean of the votes) test for n1, n2 and n3 after voting
		n1.addVote(v1);
		assertEquals(1, n1.getRating(), 0.01);
		n1.addVote(v2);
		assertEquals(0.5, n1.getRating(), 0.01);
		n2.addVote(v3);
		assertEquals(-1, n2.getRating(), 0.01);
		n2.addVote(v4);
		assertEquals(0, n2.getRating(), 0.01);
		n3.addVote(v5);
		assertEquals(0, n3.getRating(), 0.01);
		n3.addVote(v6);
		assertEquals(-0.5, n3.getRating(), 0.01);
		
		// More tests, for the sake of accuracy
		n1.addVote(v3);
		n1.addVote(v6);
		assertEquals(-0.25, n1.getRating(), 0.01);
		n2.addVote(v1);
		n2.addVote(v5);
		assertEquals(0.25, n2.getRating(), 0.01);
		n3.addVote(v2);
		n3.addVote(v4);
		assertEquals(0, n3.getRating(), 0.01);
	}
	
	@Test
	public void testGetVotes() throws Exception {
		// Getting the votes list from n1, n2 and n3 before adding any votes
		assertEquals(0, n1.getVotes().size(), 0.01);
		assertEquals(0, n2.getVotes().size(), 0.01);
		assertEquals(0, n3.getVotes().size(), 0.01);
		
		// Testing after voting
		n1.addVote(v1);
		assertEquals(1, n1.getVotes().size(), 0.01);
		n2.addVote(v2);
		n2.addVote(v3);
		assertEquals(2, n2.getVotes().size(), 0.01);
		n3.addVote(v4);
		n3.addVote(v5);
		n3.addVote(v6);
		assertEquals(3, n3.getVotes().size(), 0.01);
	}
	
	@Test
	public void testSetVotes() throws Exception {
		// Assuring n1 has no votes in the beginning of the test
		assertEquals(0, n1.getVotes().size(), 0.01);
		
		// Adding new votes to n1
		n1.addVote(v1);
		n1.addVote(v2);
		assertEquals(2, n1.getVotes().size(), 0.01);
		
		// Finally, testing the set
		ArrayList<Vote> new_votes = new ArrayList<Vote>();
		new_votes.add(v3);
		new_votes.add(v4);
		new_votes.add(v5);
		new_votes.add(v6);
		n1.setVotes(new_votes);
		assertEquals(4, n1.getVotes().size(), 0.01);
	}
	
	@Test
	public void testUserHasVoted() throws Exception {
		// Tests whether current user has voted already or not
		n2.addVote(v1);
		n2.addVote(v2);
		n2.addVote(v3);
		assertEquals(false, n2.userHasVoted(u4));
		assertEquals(false, n2.userHasVoted(u5));
		assertEquals(false, n2.userHasVoted(u6));
		assertEquals(true, n2.userHasVoted(u1));
		assertEquals(true, n2.userHasVoted(u2));
		assertEquals(true, n2.userHasVoted(u3));
	}
	
	@Test
	public void testAddVote() {
		try {
			// Testing for normal votes
			assertEquals(0, n3.getVotes().size(), 0.01);
			assertEquals(-2, n3.getRating(), 0.01);
			n3.addVote(v1);
			assertEquals(1, n3.getVotes().size(), 0.01);
			assertEquals(1, n3.getRating(), 0.01);
			n3.addVote(v2);
			assertEquals(2, n3.getVotes().size(), 0.01);
			assertEquals(0.5, n3.getRating(), 0.01);
			n3.addVote(v3);
			assertEquals(3, n3.getVotes().size(), 0.01);
			assertEquals(0, n3.getRating(), 0.01);
			n3.addVote(v4);
			assertEquals(4, n3.getVotes().size(), 0.01);
			assertEquals(0.25, n3.getRating(), 0.01);
			
			// Case in which the user has already voted for that name and tries to vote again
			n3.addVote(v1);
			fail("Did not break with second vote from same user to one name");
		} catch (Exception e) {
			assertEquals("User has already voted for this name", e.getMessage());
		}
	}

	@Test
	public void testEquals() throws Exception {
		// Case in which the names are the same
		n2 = new Name("Franco", "Origem: Latim. Significado: Homem livre");
		assertEquals(true, n1.equals(n2));
		
		// Case in which the names differ
		assertEquals(false, n1.equals(n3));
	}

	@Test
	public void testGetTags() {
		// Getting tags from n1, n2 and n3
		assertEquals(true, n1.getTags().get(0).equals(t1));
		assertEquals(true, n1.getTags().get(1).equals(t2));
		assertEquals(true, n2.getTags().get(0).equals(t1));
		assertEquals(true, n2.getTags().get(1).equals(t3));
		assertEquals(true, n3.getTags().get(0).equals(t1));
		assertEquals(true, n3.getTags().get(1).equals(t4));
	}
	
	@Test
	public void testHasTag() {
		// Case in which names has the specified tag
		assertEquals(true, n1.hasTag(t1));
		assertEquals(true, n1.hasTag(t2));
		
		// Case in which it has not
		assertEquals(false, n1.hasTag(t3));
		assertEquals(false, n1.hasTag(t4));
	}

	@Test
	public void testCompareTo() throws Exception {
		// How can I test this?
	}
}
